﻿#include "Server.h"
#include <QHostAddress>
#include <QDebug>


Server::Server(void)
{
	connect(this, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));

	//服务器每2秒检查一遍每个客户端是否心跳超时，超时就离线踢除
	mTimerCheckHeartbeat = startTimer(2000);

	//服务器每3秒通知一次各客户端房间人数状态
	mTimerUpdateRoomState = startTimer(3000);
    server=new QWebSocketServer("Server",QWebSocketServer::NonSecureMode,this);
    server->listen(QHostAddress::Any,12345);
    connect(server,&QWebSocketServer::newConnection,this,&Server::onNewConnection);
}

Server::~Server()
{
    clearClient();
    server->close();

}


bool Server::Start(int port)
{
	return listen(QHostAddress::Any, port);
}

void Server::slotNewConnection()
{
	if (hasPendingConnections())
	{
		QTcpSocket* socket = nextPendingConnection();
		if (socket->isValid())
		{
			Player *p = new Player(socket);
			connect(p, &Player::sigRecvMsg, this, &Server::slotRecvMsg);
		}
	}
}

void Server::slotRecvMsg(char *pData, int nDataLen)
{
	Player* p = (Player*)sender();
	MSG_HEADER* pHeader = (MSG_HEADER*)pData;
	OnMsg(p, pHeader, (char*)(pHeader + 1), nDataLen);
}

void Server::Log(QString text)
{
    emit sigLog(text);
    emit sendMessage(text);
    QWebSocket *socket=server->nextPendingConnection();
    connect(this,&Server::sendMessage,socket,&QWebSocket::sendTextMessage);
}

bool Server::Add( Player* p)
{
    QMutexLocker locker(&mMutex);
    mPlayers.append(p);
	return true;
}

bool Server::Remove(const QString name)
{
	QMutexLocker locker(&mMutex);
    foreach (Player* p, mPlayers)
    {
        if(p->mName == name)
        {
            mPlayers.removeOne(p);
            return true;
        }
    }
	return true;
}

bool Server::Remove(Player* p)
{
	QMutexLocker locker(&mMutex);
	mPlayers.removeOne(p);
	return true;
}

Player* Server::Find(const QString name)
{
    QMutexLocker locker(&mMutex);
    foreach (Player* p, mPlayers)
    {
        if(p->mName == name)
        {
            return p;
        }
    }
    return NULL;
}

int Server::CountPlayersInRoom(int room)
{
    QMutexLocker locker(&mMutex);
    int nCount = 0;
    foreach (Player* p, mPlayers)
    {
        if(p->mRoom == room)
        {
            nCount++;
        }
    }
    return nCount;
}


void Server::CountPlayerInRoom(int room, ROOM_STATE& s)
{
	QMutexLocker locker(&mMutex);

	QList<Player*> players;
	int nCount = 0;
	foreach(Player* p, mPlayers)
	{
		if (p->mRoom == room)
		{
			players.append(p);
			nCount++;
		}
	}

	for (int i=0;i<players.size();i++)
	{
		Player* p = players.at(i);
		if (i == 0)
		{
			strcpy(s.szName1, p->mName.toLocal8Bit());
		}
		else if (i == 1)
		{
			strcpy(s.szName2, p->mName.toLocal8Bit());
		}
	}
	s.nPlayers = nCount;
}

void Server::CheckHeartbeat()
{
	QMutexLocker locker(&mMutex);
	foreach(Player* p, mPlayers)
	{
		if (p->IsHeartbeatTimeout())
		{
			Log(QStringLiteral("客户端(%1)连接超时，断开连接").arg(p->mName));

			QString name = p->mName;
			int room = p->mRoom;
			mPlayers.removeOne(p);

			foreach(Player* pp, mPlayers)
			{
				if (pp->mRoom == room)
				{
					//给同一房间所有人发该玩家掉线消息
					MSG_HEADER msg = { 0 };
					msg.nType = MA_OFFLINE;
					strcpy(msg.szName, name.toLocal8Bit());
					p->Send(msg);
				}
			}
		}
	}
}

void Server::UpdateRoomState()
{
	//所有房间玩家状态
	QList<ROOM_STATE> states;
	for (int i = 0; i < FIR_MAX_ROOM; i++)
	{
		ROOM_STATE s;
		CountPlayerInRoom(i, s);
		states.append(s);
	}

    int nLen = FIR_MAX_ROOM * sizeof(ROOM_STATE);
    char* buf = new char[nLen];
    memset(buf, 0, nLen);

	for (int i = 0; i < states.size(); i++)
	{
		ROOM_STATE s = states.at(i);
		memcpy(buf + i * sizeof(ROOM_STATE), &s, sizeof(ROOM_STATE));
	}

    QMutexLocker locker(&mMutex);
    foreach (Player* p, mPlayers)
    {
		MSG_HEADER header = { 0 };
		header.nType = MA_UPDATEROOMSTATE;
		header.nDataLen = nLen;
		p->Send(header, buf, nLen);
    }

    delete []buf;
}

void Server::timerEvent(QTimerEvent *e)
{
	if (mTimerCheckHeartbeat == e->timerId())
	{
		CheckHeartbeat();
	}
	else if (mTimerUpdateRoomState == e->timerId())
	{
		UpdateRoomState();
	}
}

void Server::OnMsg(Player* p, MSG_HEADER* pHeader, char *pData, int nDataLen)
{
	switch (pHeader->nType)
	{
	case MQ_LOGON:			//客户端登录
	{
		OnMsgLogon(p, pHeader);
		break;
	}
	case MQ_MOVE:			//客户端走棋
	{
		OnMsgMove(p, pHeader);
		break;
	}
	case MQ_SUCCESS:		//客户端胜利
	{
		OnMsgSuccess(p, pHeader);
		break;
	}
	case MQ_SAY:            //客户端说话
	{
		OnMsgSay(p, pHeader);
		break;
	}
	case MQ_HEARTBEAT:
	{
		OnMsgHeartBeat(p, pHeader);  //客户端发来心跳
		break;
	}
	default:
	{
		break;
	}
	}
}

void Server::OnMsgLogon(Player* player, MSG_HEADER* pHeader)
{
	QString name = QString::fromLocal8Bit(pHeader->szName);
	int room = pHeader->nNum1;
	Log(QStringLiteral("客户端(%1)请求登陆：房间%2").arg(name).arg(room));

	int nCount = CountPlayersInRoom(room);
	if (nCount >= 2)
	{
		Log(QStringLiteral("客户端(%1)登陆失败！房间%2已满！").arg(name).arg(room));

		MSG_HEADER msg = { 0 };
		msg.nType = MA_LOGON;
		msg.nNum1 = 0;
		player->Send(msg);
	}
	else
	{
		if (Find(name))
		{
			Log(QStringLiteral("客户端(%1)登陆失败！用户已存在！").arg(name));
			return;
		}
		Log(QStringLiteral("客户端(%1)登陆成功！房间%2").arg(name).arg(room));

		player->UpdateHeartbeat();
		player->mName = name;
		player->mRoom = room;
		Add(player);
		nCount++;

		MSG_HEADER msg = { 0 };
		msg.nType = MA_LOGON;
		msg.nNum1 = 1;           //应答：登录成功nNum1=1/登录失败nNum1=0
		msg.nNum3 = nCount;      //当前房间人数
		strcpy(msg.szName, pHeader->szName);

		bool bFirst = true;
		foreach(Player* p, mPlayers)
		{
			if (p->mRoom == room)
			{
				//给同一房间所有人发登陆消息
				p->Send(msg);

				//如果该房间用户达到2个，给同一房间所有人发开始消息
				//并指定第一个玩家为黑棋，之后为白棋
				if (nCount >= 2)
				{
					if (bFirst)
					{
						p->mBlack = true;
						bFirst = false;
					}
					else
					{
						p->mBlack = false;
					}
					MSG_HEADER header2 = { 0 };
					header2.nType = MA_START;
					header2.nNum2 = p->mBlack ? 1 : 0;
					p->Send(header2);
				}
			}
		}
	}
}

void Server::OnMsgMove(Player* player, MSG_HEADER* pHeader)
{
	int x = pHeader->nNum1;
	int y = pHeader->nNum2;

	QString strMsg = QString(QStringLiteral("客户端(%1)走棋：(%2,%3)")).arg(player->mName).arg(x).arg(y);
	Log(strMsg);

	//给同一个房间的其他玩家发走棋消息
	foreach(Player* p, mPlayers)
	{
		if (p->mRoom == player->mRoom && p != player)
		{
			MSG_HEADER msg = { 0 };
			msg.nType = MA_MOVE;
			msg.nNum1 = x;
			msg.nNum2 = y;
			strcpy(msg.szName, player->mName.toLocal8Bit());
			p->Send(msg);
		}
	}
}

void Server::OnMsgSuccess(Player* player, MSG_HEADER* pHeader)
{
	QString str = player->mBlack ? QStringLiteral("黑棋") : QStringLiteral("白棋");

	QString strMsg = QString(QStringLiteral("客户端(%1) %2胜利.")).arg(player->mName).arg(str);
	Log(strMsg);

}

void Server::OnMsgSay(Player* player, MSG_HEADER *pHeader)
{
	QString text = QString::fromLocal8Bit(pHeader->szSay);
	QString strMsg = QString(QStringLiteral("客户端(%1)说：%2")).arg(player->mName).arg(text);
	Log(strMsg);

	//给同一个房间的所有用户发送说话消息
	foreach(Player* p, mPlayers)
	{
		if (p->mRoom == player->mRoom)
		{
			pHeader->nType = MA_SAY;
			p->Send(*pHeader);
		}
	}
}

void Server::OnMsgHeartBeat(Player* player, MSG_HEADER *pHeader)
{
	player->UpdateHeartbeat();
}


void Server::clearClient()
{
    for(int i=clientList.count()-1;i>=0;i--)
    {
        //qDebug()<<i;
        clientList.at(i)->disconnect();
        clientList.at(i)->close();
    }
    qDeleteAll(clientList);
    clientList.clear();
}

void Server::onNewConnection()
{
    QWebSocket *socket=server->nextPendingConnection();
    if(!socket)
        return;
    clientList.push_back(socket);
    connect(this,&Server::sendMessage,socket,&QWebSocket::sendTextMessage);
    //断开连接，释放
    connect(socket,&QWebSocket::disconnected,[this,socket](){
        clientList.removeAll(socket);
        socket->deleteLater();
    });
}


